LEÇON 10

Projet Final : Application de Gestion de Tâches

Mise en pratique de tous les concepts appris

Français - Projet To-Do List

Objectif du projet : Créer une application complète de gestion de tâches (To-Do List) qui intègre les concepts essentiels de Python : programmation orientée objet, gestion de fichiers, exceptions, tests, et interface en ligne de commande.

1. Aperçu du projet

Nous allons construire une application de gestion de tâches avec les fonctionnalités suivantes :

  • Ajouter, modifier et supprimer des tâches
  • Marquer une tâche comme complétée
  • Persistance des données (sauvegarde dans un fichier JSON)
  • Affichage des tâches (toutes, complétées, en cours)
  • Tests unitaires pour valider le fonctionnement
  • Interface en ligne de commande intuitive
Structure du projet :
todo_project/

├── todo.py # Modèle de données et classes principales
├── storage.py # Gestion de la persistance (JSON)
├── cli.py # Interface utilisateur
├── tests.py # Tests unitaires
└── data/
    └── tasks.json # Fichier de données

2. Modèle de données (todo.py)

# todo.py - Classe représentant une tâche et le gestionnaire
from datetime import datetime
import uuid

class Tache:
    """Classe représentant une tâche individuelle"""
    
    def __init__(self, titre, description=""):
        self.id = str(uuid.uuid4())[:8] # ID unique
        self.titre = titre
        self.description = description
        self.complete = False
        self.date_creation = datetime.now().isoformat()
        self.date_completion = None

    def marquer_completee(self):
        """Marque la tâche comme complétée"""
        if not self.complete:
            self.complete = True
            self.date_completion = datetime.now().isoformat()

    def to_dict(self):
        """Convertit la tâche en dictionnaire pour JSON"""
        return {
            "id": self.id,
            "titre": self.titre,
            "description": self.description,
            "complete": self.complete,
            "date_creation": self.date_creation,
            "date_completion": self.date_completion
        }

    @classmethod
    def from_dict(cls, data):
        """Crée une tâche à partir d'un dictionnaire"""
        tache = cls(data["titre"], data["description"])
        tache.id = data["id"]
        tache.complete = data["complete"]
        tache.date_creation = data["date_creation"]
        tache.date_completion = data["date_completion"]
        return tache

    def __str__(self):
        statut = "✓" if self.complete else "○"
        return f"[{statut}] {self.id} - {self.titre}"
# Suite - Gestionnaire de tâches
class GestionnaireTaches:
    """Gère la collection de tâches"""
    
    def __init__(self):
        self.taches = []

    def ajouter(self, titre, description=""):
        """Ajoute une nouvelle tâche"""
        tache = Tache(titre, description)
        self.taches.append(tache)
        return tache

    def supprimer(self, tache_id):
        """Supprime une tâche par son ID"""
        tache = self.trouver_par_id(tache_id)
        if tache:
            self.taches.remove(tache)
            return True
        return False

    def marquer_completee(self, tache_id):
        """Marque une tâche comme complétée"""
        tache = self.trouver_par_id(tache_id)
        if tache:
            tache.marquer_completee()
            return True
        return False

    def trouver_par_id(self, tache_id):
        """Trouve une tâche par son ID"""
        for tache in self.taches:
            if tache.id == tache_id:
                return tache
        return None

    def lister_toutes(self):
        """Retourne toutes les tâches"""
        return self.taches

    def lister_completes(self):
        """Retourne les tâches complétées"""
        return [t for t in self.taches if t.complete]

    def lister_en_cours(self):
        """Retourne les tâches non complétées"""
        return [t for t in self.taches if not t.complete]

3. Persistance des données (storage.py)

# storage.py - Sauvegarde et chargement des données
import json
import os
from todo import Tache, GestionnaireTaches

class StockageJSON:
    """Gère la persistance des tâches dans un fichier JSON"""
    
    def __init__(self, chemin_fichier="data/tasks.json"):
        self.chemin_fichier = chemin_fichier
        self._assurer_dossier()

    def _assurer_dossier(self):
        """Crée le dossier data s'il n'existe pas"""
        dossier = os.path.dirname(self.chemin_fichier)
        if dossier and not os.path.exists(dossier):
            os.makedirs(dossier)

    def sauvegarder(self, gestionnaire):
        """Sauvegarde toutes les tâches dans le fichier JSON"""
        try:
            donnees = [tache.to_dict() for tache in gestionnaire.lister_toutes()]
            with open(self.chemin_fichier, 'w', encoding='utf-8') as f:
                json.dump(donnees, f, indent=2, ensure_ascii=False)
            return True
        except Exception as e:
            print(f"Erreur lors de la sauvegarde: {e}")
            return False

    def charger(self):
        """Charge les tâches depuis le fichier JSON"""
        gestionnaire = GestionnaireTaches()
        
        if not os.path.exists(self.chemin_fichier):
            return gestionnaire
        
        try:
            with open(self.chemin_fichier, 'r', encoding='utf-8') as f:
                donnees = json.load(f)
                for item in donnees:
                    tache = Tache.from_dict(item)
                    gestionnaire.taches.append(tache)
        except Exception as e:
            print(f"Erreur lors du chargement: {e}")
        
        return gestionnaire

4. Interface utilisateur (cli.py)

# cli.py - Interface en ligne de commande
import sys
from todo import GestionnaireTaches
from storage import StockageJSON

def afficher_menu():
    print("\n" + "="*50)
    print("📋 GESTIONNAIRE DE TÂCHES")
    print("="*50)
    print("1. Ajouter une tâche")
    print("2. Lister toutes les tâches")
    print("3. Lister les tâches en cours")
    print("4. Lister les tâches complétées")
    print("5. Marquer une tâche comme complétée")
    print("6. Supprimer une tâche")
    print("7. Quitter")
    print("-"*50)

def afficher_taches(taches, titre="Tâches"):
    print(f"\n{titre}:")
    if not taches:
        print(" Aucune tâche.")
        return
    for tache in taches:
        statut = "✓" if tache.complete else "○"
        print(f" [{statut}] {tache.id} : {tache.titre}")
        if tache.description:
            print(f" 📝 {tache.description}")

def main():
    """Fonction principale de l'application"""
    stockage = StockageJSON()
    gestionnaire = stockage.charger()
    
    print("Bienvenue dans votre gestionnaire de tâches!")
    
    while True:
        afficher_menu()
        choix = input("Votre choix: ").strip()
        
        if choix == "1":
            titre = input("Titre de la tâche: ").strip()
            if titre:
                description = input("Description (optionnelle): ").strip()
                tache = gestionnaire.ajouter(titre, description)
                stockage.sauvegarder(gestionnaire)
                print(f"✅ Tâche ajoutée (ID: {tache.id})")
            else:
                print("❌ Le titre ne peut pas être vide.")
        
        elif choix == "2":
            afficher_taches(gestionnaire.lister_toutes(), "Toutes les tâches")
        
        elif choix == "3":
            afficher_taches(gestionnaire.lister_en_cours(), "Tâches en cours")
        
        elif choix == "4":
            afficher_taches(gestionnaire.lister_completes(), "Tâches complétées")
        
        elif choix == "5":
            tache_id = input("ID de la tâche à compléter: ").strip()
            if gestionnaire.marquer_completee(tache_id):
                stockage.sauvegarder(gestionnaire)
                print("✅ Tâche marquée comme complétée!")
            else:
                print("❌ Tâche non trouvée.")
        
        elif choix == "6":
            tache_id = input("ID de la tâche à supprimer: ").strip()
            if gestionnaire.supprimer(tache_id):
                stockage.sauvegarder(gestionnaire)
                print("🗑️ Tâche supprimée!")
            else:
                print("❌ Tâche non trouvée.")
        
        elif choix == "7":
            print("👋 Au revoir!")
            stockage.sauvegarder(gestionnaire)
            break
        
        else:
            print("❌ Choix invalide. Veuillez réessayer.")

if __name__ == "__main__":
    main()

5. Tests unitaires (tests.py)

# tests.py - Tests unitaires pour l'application
import unittest
import tempfile
import os
from todo import Tache, GestionnaireTaches
from storage import StockageJSON

class TestTache(unittest.TestCase):
    def test_creation_tache(self):
        tache = Tache("Test", "Description test")
        self.assertEqual(tache.titre, "Test")
        self.assertEqual(tache.description, "Description test")
        self.assertFalse(tache.complete)
        self.assertIsNotNone(tache.id)

    def test_marquer_completee(self):
        tache = Tache("Test")
        tache.marquer_completee()
        self.assertTrue(tache.complete)
        self.assertIsNotNone(tache.date_completion)

    def test_conversion_dict(self):
        tache = Tache("Test", "Desc")
        tache.marquer_completee()
        d = tache.to_dict()
        tache2 = Tache.from_dict(d)
        self.assertEqual(tache.id, tache2.id)
        self.assertEqual(tache.titre, tache2.titre)
        self.assertEqual(tache.complete, tache2.complete)

class TestGestionnaireTaches(unittest.TestCase):
    def setUp(self):
        self.gestionnaire = GestionnaireTaches()
        self.tache1 = self.gestionnaire.ajouter("Tâche 1")
        self.tache2 = self.gestionnaire.ajouter("Tâche 2")

    def test_ajouter(self):
        self.assertEqual(len(self.gestionnaire.lister_toutes()), 2)

    def test_supprimer(self):
        resultat = self.gestionnaire.supprimer(self.tache1.id)
        self.assertTrue(resultat)
        self.assertEqual(len(self.gestionnaire.lister_toutes()), 1)

    def test_marquer_completee(self):
        resultat = self.gestionnaire.marquer_completee(self.tache1.id)
        self.assertTrue(resultat)
        self.assertTrue(self.tache1.complete)
        self.assertEqual(len(self.gestionnaire.lister_en_cours()), 1)
        self.assertEqual(len(self.gestionnaire.lister_completes()), 1)

class TestStockage(unittest.TestCase):
    def test_sauvegarde_et_chargement(self):
        # Créer un fichier temporaire
        with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp:
            tmp_path = tmp.name
        
        # Sauvegarder
        gestionnaire = GestionnaireTaches()
        gestionnaire.ajouter("Test sauvegarde")
        stockage = StockageJSON(tmp_path)
        stockage.sauvegarder(gestionnaire)
        
        # Charger
        nouveau_gestionnaire = stockage.charger()
        self.assertEqual(
            len(gestionnaire.lister_toutes()),
            len(nouveau_gestionnaire.lister_toutes())
        )
        
        # Nettoyer
        os.remove(tmp_path)

if __name__ == '__main__':
    unittest.main()
Pour aller plus loin :
  • ➕ Ajoutez des catégories ou des étiquettes aux tâches
  • 📅 Ajoutez des dates d'échéance et des rappels
  • 🎨 Créez une interface graphique avec Tkinter ou PyQt
  • ☁️ Synchronisez les tâches avec un serveur (API REST)
  • 📧 Envoyez des rappels par email
  • 🔍 Ajoutez une fonction de recherche
Félicitations ! Vous avez complété les 10 leçons Python. Vous maîtrisez maintenant les bases essentielles du langage. Continuez à pratiquer et à explorer de nouveaux projets pour progresser !

English - Final Project: To-Do List App

Project Objective: Create a complete task management application (To-Do List) that integrates essential Python concepts: OOP, file handling, exceptions, testing, and command-line interface.

1. Project Overview

  • Add, edit, and delete tasks
  • Mark tasks as completed
  • Data persistence (JSON file storage)
  • Display tasks (all, completed, pending)
  • Unit tests to validate functionality
  • Intuitive command-line interface
Project Structure:
todo_project/

├── todo.py # Data model and main classes
├── storage.py # JSON persistence
├── cli.py # User interface
├── tests.py # Unit tests
└── data/
    └── tasks.json # Data file

2. Data Model (todo.py)

# todo.py - Task and Manager classes (see French section for full code)
class Task:
    """Class representing an individual task"""
    def __init__(self, title, description=""):
        self.id = str(uuid.uuid4())[:8]
        self.title = title
        self.description = description
        self.completed = False

    def mark_completed(self):
        self.completed = True

3. Running the Application

# To run the application:
$ python cli.py

# To run tests:
$ python tests.py
$ python -m unittest tests.py

# Example interaction:
==================================================
📋 TASK MANAGER
==================================================
1. Add a task
2. List all tasks
3. List pending tasks
4. List completed tasks
5. Mark a task as completed
6. Delete a task
7. Quit
--------------------------------------------------
Your choice: 1
Task title: Learn Python
Description (optional): Complete all 10 lessons
✅ Task added (ID: a1b2c3d4)
Next Steps:
  • ➕ Add categories or tags to tasks
  • 📅 Add due dates and reminders
  • 🎨 Create a GUI with Tkinter or PyQt
  • ☁️ Sync tasks with a server (REST API)
  • 📧 Send email reminders
  • 🔍 Add search functionality
Congratulations! You have completed the 10 Python lessons. You now master the essential concepts of the language. Keep practicing and exploring new projects to improve!